package com.marion.mzuul.service;

import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.marion.mzuul.common.RequestLog;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class UserService {

    @Resource(name = "appEhCache")
    private org.ehcache.Cache appEhCache;

    private static final String NO_CACHE_RESOURCE = "userNameNoCache";

    @Autowired
    private StringRedisTemplate redisTemplate;

    private final static Cache<Object, Object> build = CacheBuilder.newBuilder()
            .expireAfterWrite(5, TimeUnit.SECONDS)
            .build();

    @PostConstruct
    public void init() {
        initSystemRule();
    }

    /**
     * guava cache set user name
     */
    public Object editName(long uid, String name) {
        build.put(uid, name);
        return build.asMap();
    }

    public Object userName(long uid) {
        long startTime = System.currentTimeMillis();
        Object value = build.asMap().get(uid);
        if (value == null) {
            // TODO: 2021/10/15 模拟数据库查询操作
            value = userNameNoCache(uid);
            build.put(uid, value);
        }
        long endTime = System.currentTimeMillis();
        log.info("guava value={} exe={}m", value, endTime - startTime);
        Object ret = value;
        return ret;
    }

    @SentinelResource(value = NO_CACHE_RESOURCE, blockHandler = "userNameBlock", fallback = "userNameDegrade")
    public String userNameNoCache(long uid) {

        /**
         * 1. 定义资源的三种方式
         *      1. SphU.entry("资源名")
         *      2. try-with-resources
         *      3. @SentinelResource注解
         * 2. 定义规则：
         *      1. 流控规则
         *      2. 熔断降级规则
         *      3. 系统规则
         *      4. 权限规则
         *      5. 热点参数规则
         *  3. 降级策略
         *      1. RT
         *      2. 异常比例
         *      3. 异常数
         *  4. 流控策略
         *  5. 系统规则模式
         *      1. Load自适应
         */

        if (uid == 0) {
            throw new NullPointerException();
        }

        try {
            Thread.sleep(100L);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "name";
    }

    public String userNameBlock(long uid, BlockException e) {
        log.info("userNameBlock e={}", e.getRule());
        return uid + ":limited";
    }

    public String userNameDegrade(long uid, BlockException e) {
        log.info("userNameBlock e={}", e.getRule());
        return uid + ":degrade";
    }

    public Object ehcache(long uid) {
        long startTime = System.currentTimeMillis();
        Object value = appEhCache.get(String.valueOf(uid));
        if (value == null) {
            // TODO: 2021/10/15 模拟数据库查询操作
            value = userNameNoCache(uid);
            appEhCache.put(String.valueOf(uid), value);
        }
        long endTime = System.currentTimeMillis();
        Object ret = value;
        log.info("ehcache value={} exe={}m", value, endTime - startTime);
        return ret;
    }

    @RequestLog
    @SentinelResource(value = "redis")
    public Object redis(long uid) {
        ValueOperations<String, String> val = redisTemplate.opsForValue();
        String key = "redis:" + uid;
        String value = val.get(key);

        if (uid == 0) {
            throw new IllegalStateException();
        }

        if (value == null) {
            // TODO: 2021/10/15 模拟数据库查询操作
            value = userNameNoCache(uid);
            val.set(key, value);
            redisTemplate.expire(key, 10L, TimeUnit.SECONDS);
        }
        return value;
    }

    private void initSystemRule() {
//        List<SystemRule> rules = new ArrayList<>();
//        SystemRule rule = new SystemRule();
//        rule.setQps(1);
//        rules.add(rule);
//        // 所有接口流控都是1
//        SystemRuleManager.loadRules(rules);

        // 流控规则
        List<FlowRule> flowRules = new ArrayList<>();
        FlowRule flowRule = new FlowRule();
        flowRule.setResource(NO_CACHE_RESOURCE);
        flowRule.setCount(1);
        flowRule.setStrategy(RuleConstant.STRATEGY_DIRECT);

        flowRules.add(flowRule);
        FlowRuleManager.loadRules(flowRules);

        // 降级规则
        List<DegradeRule> degradeRules = new ArrayList<>();
        DegradeRule degradeRule = new DegradeRule();
        degradeRule.setResource(NO_CACHE_RESOURCE);
        degradeRule.setCount(1);
        degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
        degradeRule.setMinRequestAmount(1);
        degradeRule.setTimeWindow(5);

        degradeRules.add(degradeRule);
        DegradeRuleManager.loadRules(degradeRules);
    }

}
